#include "Settings.hpp"

/*static*/ Settings& Settings::Load(std::string filename)
{
    for (unsigned int i = 0; i < settingsList.size(); i++)
    {
        if (filename.compare(settingsList[i]->Filename())==0)
            return (*settingsList[i]);
    }
    // If we're here, we need to open a new Settings
    Settings* mySettings = new Settings(filename);
    settingsList.push_back(mySettings);
    return (*mySettings);
}

bool Settings::IsEmpty()
{
    return (boolSettings.empty() && intSettings.empty() && 
            doubleSettings.empty() && stringSettings.empty());
}

bool Settings::Save()
{
    std::ofstream out;
    out.open(filename.c_str(), std::ofstream::out);
    if(!out.good())
        return false;
    
    out << "[settings]\n";
    std::map<std::string, bool>::iterator boolIT = boolSettings.begin();
    while(boolIT != boolSettings.end())
    {
        out << "bool\n";
        out << boolIT->first << "\n";
        if (boolIT->second)
            out << "true\n";
        else
            out << "false\n";
        out << "\n";
        boolIT++;
    }
    std::map<std::string, int>::iterator intIT = intSettings.begin();
    while (intIT != intSettings.end())
    {
        out << "int\n";
        out << intIT->first << "\n";
        out << intIT->second << "\n";
        out << "\n";
        intIT++;
    }
    std::map<std::string, double>::iterator doubleIT = doubleSettings.begin();
    while (doubleIT != doubleSettings.end())
    {
        out << "double\n";
        out << doubleIT->first << "\n";
        out << doubleIT->second << "\n";
        out << "\n";
        doubleIT++;
    }
    std::map<std::string, std::string>::iterator stringIT = stringSettings.begin();
    while (stringIT != stringSettings.end())
    {
        out << "string\n";
        out << stringIT->first << "\n";
        out << stringIT->second << "\n";
        out << "\n";
        stringIT++;
    }

    out.close();
    return true;
}


std::string Settings::Filename()
{
    return filename;
}

bool Settings::Contains(std::string key)
{
    if (boolSettings.count(key) > 0)
        return true;
    if (intSettings.count(key) > 0)
        return true;
    if (doubleSettings.count(key) > 0)
        return true;
    if (stringSettings.count(key) > 0)
        return true;
    return false;
}

bool Settings::GetBool(std::string key, bool defaultValue)
{
    if (boolSettings.count(key) > 0)
        return boolSettings[key];
    return defaultValue;
}

int Settings::GetInt(std::string key, int defaultValue)
{
    if (intSettings.count(key) > 0)
        return intSettings[key];
    return defaultValue;
}

double Settings::GetDouble(std::string key, double defaultValue)
{
    if (doubleSettings.count(key) > 0)
        return doubleSettings[key];
    return defaultValue;
}

std::string Settings::GetString(std::string key, std::string defaultValue)
{
    if (stringSettings.count(key) > 0)
        return stringSettings[key];
    return defaultValue;
}

void Settings::SetBool(std::string key, bool value)
{
    boolSettings[key] = value;
}

void Settings::SetInt(std::string key, int value)
{
    intSettings[key] = value;
}

void Settings::SetDouble(std::string key, double value)
{
    doubleSettings[key] = value;
}

void Settings::SetString(std::string key, std::string value)
{
    stringSettings[key] = value;
}

std::deque<Settings*> Settings::settingsList;

/*static*/ std::string Settings::EliminateWhitespace(std::string in)
{
    std::string out;
    for (unsigned int i = 0; i < in.length(); i++)
    {
        if (in[i] != ' ' && in[i] != '\t')
            out.push_back(in[i]);
    }
    return out;
}


// private constructor
Settings::Settings(std::string filename)
{
    if (!LoadFile(filename))
        std::cout << "Loading of \"" << filename << "\" failed!\n";
}


bool Settings::LoadFile(std::string filename)
{
    std::ifstream file;
    this->filename = filename;
    file.open(filename.c_str(), std::ifstream::in);
    if (!file.good())
        return false;
    
    char c_line[100];    // max 100 chars per line
    std::string line;
    file.getline(c_line, 100);
    line = std::string(c_line);
    line = EliminateWhitespace(line);
    if (line.compare("[settings]") != 0)
    {
        std::cout << "Line 1 was not expected header!\n";
        file.close();
        return false;
    }
    int lineNumber = 2; // if we got here, we read from line 2
    std::string key;
    while (!file.eof())
    {
        file.getline(c_line,100);
        line = std::string(c_line);
        line = EliminateWhitespace(line);
        lineNumber++;
        
        // skip just empty lines
        if (line.length() == 0)
            continue;
        if (line.compare("bool") == 0)
        {   // we have a boolean value
            // first read in the key, whole line, including whitespace
            file.getline(c_line,100);
            line = std::string(c_line);
            key = line;
            
            // now read in the value, eliminating whitespace
            file.getline(c_line,100);
            line = std::string(c_line);
            line = EliminateWhitespace(line);
            if (line.compare("true") == 0)
                boolSettings[key] = true;
            else
                boolSettings[key] = false;

        }
        else if (line.compare("int") == 0)
        {   // we have an integer
            // first read in the key, whole line, including whitespace
            file.getline(c_line,100);
            line = std::string(c_line);
            key = line;

            // now read a line, eliminating whitespace
            file.getline(c_line,100);
            line = std::string(c_line);
            line = EliminateWhitespace(line);
            int num = (int)sToF(line);
            intSettings[key] = num;
            
        }
        else if (line.compare("double") == 0)
        { // we have a double
            // first read in the key, whole line, including whitespace
            file.getline(c_line,100);
            line = std::string(c_line);
            key = line;

            // now read a line, eliminating whitespace
            file.getline(c_line,100);
            line = std::string(c_line);
            line = EliminateWhitespace(line);

            double number = sToF(line);
            doubleSettings[key] = number;

        }
        else if (line.compare("string") == 0)
        { // we have a string
            // first read in the key, whole line, including whitespace
            file.getline(c_line,100);
            line = std::string(c_line);
            key = line;

            // now read a whole line again, WITH whitespace
            file.getline(c_line,100);
            line = std::string(c_line);
            
            stringSettings[key] = line;
        }
        else 
        {
            std::cout << filename << "\n [WARNING] Unrecognized token \n[WARNING] \"" << line << "\" \n[WARNING]  at line " << lineNumber << "\n";
        }

    }

    file.close();
    return true;
}
